package cz.cvut.fit.crhonjar.mi.paa.dunedin;

import java.util.ArrayList;
import java.util.List;

import cz.cvut.fit.crhonjar.mi.paa.dunedin.exception.DunedinException;

public class State implements Cloneable {

	private final List<Bucket> buckets;
	private int depth;
	private State previous;

	public State(final List<Bucket> buckets) {
		if (buckets == null) {
			throw new IllegalArgumentException(
					"State cannot be innitialized with NULL buckets");
		}
		this.buckets = buckets;
		this.depth = 0;
	}

	@Override
	public State clone() {
		final State s = new State(new ArrayList<Bucket>());
		for (final Bucket b : this.getBuckets()) {
			s.getBuckets().add(b.clone());
		}
		s.depth = this.depth;
		s.previous = this;
		return s;

	}

	public Bucket getBucket(final int index) {
		return this.buckets.get(index);
	}

	public List<Bucket> getBuckets() {
		return this.buckets;
	}

	public int getCountOfBuckets() {
		return this.buckets.size();
	}

	public int getDepth() {
		return this.depth;
	}

	public State getPrevious() {
		return this.previous;
	}

	@Override
	public int hashCode() {
		int hash = 0;
		for (int i = 0; i < this.buckets.size(); i++) {
			hash *= (this.buckets.get(i).getCapacity() + 1);
			hash += this.buckets.get(i).getActual();
		}
		return hash;
	}

	public void incrementDepth() {
		this.depth++;
	}

	public boolean isFinalStateReached() {
		for (final Bucket b : this.buckets) {
			if (!b.isFinalVolumeReached()) {
				return false;
			}
		}
		return true;
	}

	public int maxHashCode() {
		int hash = 0;
		for (int i = 0; i < this.buckets.size(); i++) {
			hash *= (this.buckets.get(i).getCapacity() + 1);
			hash += this.buckets.get(i).getCapacity();
		}
		return hash;
	}

	public void pour(final Bucket b, final Bucket c) throws DunedinException {
		b.pourTo(c);
	}

	public void print() {
		System.out.print("State: [" + this.depth + "] Capacities [");
		for (final Bucket b : this.buckets) {
			System.out.print(b.getCapacity() + " ");
		}
		System.out.print("] Actual [");
		for (final Bucket b : this.buckets) {
			System.out.print(b.getActual() + " ");
		}
		System.out.print("] Final [");
		for (final Bucket b : this.buckets) {
			System.out.print(b.getFinalVolume() + " ");
		}
		System.out.print("] Hash: ["
				+ this.hashCode()
				+ "] Previous hash: ["
				+ (this.getPrevious() == null ? "none" : this.getPrevious()
						.hashCode()) + "]\n");
	}
}
